import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoServer {
    private ServerSocket serverSocket = null;

    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);

    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        while (true){
            //这里accept()方法是把已建立好连接的客户端从系统内核中拿到应用层
            Socket clientSocket =  serverSocket.accept();
            //这里同一时间可以连接多个服务端但只能处理一个客户端发来的请求
//            processConnection(clientSocket);
            //这里可以用多线程来处理多个客户端的请求
//            Thread t = new Thread(() ->{
//                processConnection(clientSocket);
//            });
//            t.start();
            //用线程池会比多线程好一点
            ExecutorService service = Executors.newCachedThreadPool();
            service.submit(new Runnable() {
                @Override
                public void run() {
                    processConnection(clientSocket);
                }
            });
        }
    }
    //通过这个方法来处理客户端的请求
    private void processConnection(Socket clientSocket) {
        //先打印一个日志
        System.out.printf("[%s:%s]客户端上线\n",clientSocket.getInetAddress(),clientSocket.getPort());
        //接下来就进行数据的交互
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream  = clientSocket.getOutputStream();
            Scanner scanner = new Scanner(inputStream);
            //这里使用try是为了防止后面忘记关闭流对象
        ) {
            //这里客户端可能会发来多条数据所以使用循环
            while (true){
                //如果连接断开了就应该跳出循环
                if(!scanner.hasNext()){
                    System.out.printf("[%s:%s]客户端下线",clientSocket.getInetAddress(),clientSocket.getPort());
                    break;
                }
                //1.读取请求并解析，这里使用next()是以“空白符”就返回
                String request = scanner.next();
                //2.计算请求
                String responce = process(request);
                //3.把响应写回到客户端
                //可以将响应转换成字节数组
                //也可以用printwriter将outputStream包裹一下
                PrintWriter printWriter = new PrintWriter(outputStream);
                //此处的 println 不是打印到控制台了, 而是写入到 outputStream 对应的流对象中, 也就是写入到 clientSocket 里面.
                //自然这个数据也就通过网络发送出去了. (发给当前这个连接的另外一端)
                //此处使用 println 带有 \n 也是为了后续 客户端这边 可以使用 scanner.next 来读取数据.
                printWriter.println(responce);
                //刷新缓冲区，如果没有刷新则数据可能还留在内存里，没有被写入网卡
                printWriter.flush();
                //4.打印交互内容日志
                System.out.printf("[%s:%s] req=%s,res=%s\n",clientSocket.getInetAddress(),clientSocket.getPort(),
                        request,responce);
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //这个方法processConnection就是在处理每个客户端连接，执行到这说明连接以处理完了
                //关闭客户端资源
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(9090);
        server.start();
    }
}
